require 'config'
require 'common.class'
local skynet = require "skynet"
local socket = require "skynet.socket"
local protobuf = require "protobuf"

tcpAgent = class('tcpAgent')

function tcpAgent:ctor()
    self.WATCHDOG = nil
    self.client_fd = nil
    self.pingReceived = false
end

function tcpAgent:Start()
    local function send_package(msgID, pack)
        local message = {
            messageType = msgID,
            code = 'SUCCESS',
            data = pack,
        }
        local bytes = protobuf.encode("Protocol", message)
        local package = string.pack(">s2", bytes)
        socket.write(self.client_fd, package)
    end

    local function send_exception(msgID, code)
        local message = {
            messageType = msgID,
            code = code,
        }
        local bytes = protobuf.encode("Protocol", message)
        local package = string.pack(">s2", bytes)
        socket.write(self.client_fd, package)
    end

    skynet.register_protocol {
        name = "client",
        id = skynet.PTYPE_CLIENT,
        unpack = function (msg, sz)
            skynet.ignoreret()
            self.pingReceived = true
            local message = skynet.tostring(msg, sz)
            local gameProtocol, reason = protobuf.decode('Protocol', message)
            local msgID = gameProtocol.messageType
            local content = gameProtocol.data
            return msgID, content
        end,
        dispatch = function (fd, _, msgID, content)
            assert(fd == self.client_fd)	-- You can use fd to reply message
            skynet.ignoreret()	-- session is fd, don't call skynet.ret
            local ok, result = nil
            if self[msgID] then
                ok, result = self[msgID](self, content)
            end

            if not ok then
                send_exception(msgID, result)
            elseif result then
                send_package(msgID, result)
            end
        end
    }

    local CMD = {}
    function CMD.start(conf)
        local fd = conf.client
        local gate = conf.gate
        self.WATCHDOG = conf.watchdog
        self.client_fd = fd
        skynet.call(gate, "lua", "forward", fd)
    end

    function CMD.disconnect()
        pcall(function() self:OnDisconnect() end)
        skynet.exit()
    end

    local function close_agent()
        skynet.call(self.WATCHDOG, "lua", "close", self.client_fd)
    end

    function CMD.kick()
        pcall(function() self:OnKick() end)
        close_agent()
    end

    skynet.start(function()
        --定时发送ping
        skynet.fork(function()
            while true do
                skynet.sleep(100 * TCP_PING_INTERVAL)
                local message = {
                    messageType = 'PING',
                    code = 'SUCCESS'
                }
                local bytes = protobuf.encode("Protocol", message)
                local package = string.pack(">s2", bytes)
                socket.write(self.client_fd, package)
            end
        end)

        skynet.fork(function()
            while true do
                skynet.sleep(100 * (2 * TCP_PING_INTERVAL + 1))
                if not self.pingReceived then
                    break
                else
                    self.pingReceived = false
                end
            end
            close_agent()
        end)

        skynet.dispatch("lua", function(_,_, command, ...)
            local f = CMD[command]
            skynet.ret(skynet.pack(f(...)))
        end)
    end)
end

function tcpAgent:OnDisconnect() end
function tcpAgent:OnKick() end